/*
 * NyuRange.cpp
 *
 *  Created on: 2010/05/29
 *      Author: mitsu
 */

#include "NyuRange.hpp"
#include "NyuString.hpp"

#include <iostream>

NyuRange::NyuRange(NyuObject* proto, NyuValue start, NyuValue end,
                   NyuValue step, bool is_include_border) : NyuObject(proto) {
    addSlot(&NyuStaticString::START, start, 0);
    addSlot(&NyuStaticString::END, end, 0);
    addSlot(&NyuStaticString::STEP, step, 0);
    addSlot(&NyuStaticString::BORDER, NYU_INT_TO_VALUE(is_include_border), 0);
}

NyuRange::~NyuRange() {
    // TODO Auto-generated destructor stub
}

NyuValue NyuRange::each_func(NyuContext* c) {
    NyuValue target = c->current_argv(0);
    if (!NYU_VALUE_IS_OBJECT(target))
        return 0;

    NyuValue callable = c->current_argv(1);
    // TODO callableなオブジェクトも許可する
    if (!NYU_VALUE_IS_NATIVE_OR_BLOCK(callable))
        return 0;

    NyuObject* self = NYU_VALUE_UNTAG_OBJECT(target);
    NyuValue start = self->getSlot(&NyuStaticString::START, c);
    NyuValue end = self->getSlot(&NyuStaticString::END, c);
    NyuValue step = self->getSlot(&NyuStaticString::STEP, c);

    // TODO オブジェクトにも対応する
    NyuInt i = NYU_VALUE_TO_INT(start);
    NyuInt j = NYU_VALUE_TO_INT(end);
    NyuInt k = NYU_VALUE_TO_INT(step);

    if (!k)
        k = 1;

    if (j < i)
        k = -k;

    c->push_scope(callable, 0);

    NyuValue* new_argv = c->current_argv();

    NyuValue ret = 0;
    switch (NYU_VALUE_TAG(callable)) {
    case NYU_VALUE_TAG_INT:
        break;

    case NYU_VALUE_TAG_NATIVE:
        for (; i < j; i += k) {
            new_argv[1] = NYU_INT_TO_VALUE(i);
            ret = NYU_VALUE_UNTAG_NATIVE(callable)(c);
        }
        break;

    case NYU_VALUE_TAG_OBJECT:
        break;

    case NYU_VALUE_TAG_BLOCK:
        {
            NyuTreeList* stmts = NYU_VALUE_UNTAG_BLOCK(callable);
            if (stmts) {
                for (; i < j; i += k) {
                    new_argv[1] = NYU_INT_TO_VALUE(i);

                    NyuTreeList* l = stmts;
                    for (; l->next(); l = l->next()) {
                        l->value()->eval(c);
                    }

                    ret = l->value()->eval(c);
                }
            }
        }
        break;
    }

    c->pop_scope();

    return 0;
}
